/*
 * Copyright (C) 2008 Peter Kling
 * 
 * This file is part of 'A Kind of Billiard', a graphical billiard simulation.
 * 
 * 'A Kind of Billiard' is free software: you can redistribute it and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * 
 * 'A Kind of Billiard' is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with 'A Kind of Billiard'. If not, see
 * <http://www.gnu.org/licenses/>.
 */

package de.akob.physics;

import java.util.ArrayList;

import de.akob.mathematics.MathPlane3D;
import de.akob.mathematics.MathVector;

/**
 * A simple physical obstacle that can model planes with holes or boundes planes or similar. Note that this kind of
 * obstacle is designed quite simple: you define the obstacle by a plane and several cuboids that define the 'holes' in
 * the plane (the cuboids can be unbounded by use of POSITIVE_INFINITY/NEGATIVE_INFINITY). This approach is neither very
 * general nor elegant, but allows easy and efficient construction/collision handling for simple obstacles.
 */
public class SimpleObstacle {
	/**
	 * Data structure for the cuboids.
	 * <p>
	 * Note: Cuboids are always considered as closed sets.
	 */
	protected class Cuboid {
		public double x_min = 0.;
		public double x_max = 0.;
		public double y_min = 0.;
		public double y_max = 0.;
		public double z_min = 0.;
		public double z_max = 0.;
		
		/**
		 * Constructs a cuboid of the given dimensions.
		 */
		public Cuboid(double x_min, double x_max, double y_min, double y_max, double z_min, double z_max) {
			this.x_min = x_min;
			this.x_max = x_max;
			this.y_min = y_min;
			this.y_max = y_max;
			this.z_min = z_min;
			this.z_max = z_max;
		}
		
		/**
		 * Returns whether the given (3-dimensional) point is contained within this cuboid.
		 * 
		 * @throws IllegalArgumentException if the given point does not have dimension 3.
		 */
		public boolean contains(MathVector p) {
			if (p.dim != 3)
				throw new IllegalArgumentException("Cuboids can only check whether 3-dim points are contained.");
			if ( /* check whether the point lies outside of one of the dimensions */
			p.get(1) < x_min || p.get(1) > x_max || p.get(2) < y_min || p.get(2) > y_max || p.get(3) < z_min
			        || p.get(3) > z_max)
				return false;
			return true;
		}
	}
	
	// data that defines the obstacle
	private final MathPlane3D       plane;
	private final ArrayList<Cuboid> cuboids = new ArrayList<Cuboid>();
	
	/**
	 * Constructs a obstacle that just consists of the given plane. Use addHole to customize the obstacle.
	 * 
	 * @see de.akob.mathematics.MathPlane3D
	 * @see #addHole
	 */
	public SimpleObstacle(MathVector normal, double d) {
		plane = new MathPlane3D(normal, d);
	}
	
	/**
	 * Customize this obstacle by defining a 'hole'. Inside such 'hole-cuboids', no collision handling will be
	 * performed. You can use Double.POSITIVE_INFINITY and Double.NEGATIVE_INFINITY to define unbounded holes.
	 */
	public void addHole(double x_min, double x_max, double y_min, double y_max, double z_min, double z_max) {
		cuboids.add(new Cuboid(x_min, x_max, y_min, y_max, z_min, z_max));
	}
	
	/**
	 * Returns the distance of the underlying plane to the origin.
	 */
	public double getDistance() {
		return plane.getDistance();
	}
	
	/**
	 * Returns the collection of holes defining this obstacle.
	 */
	public ArrayList<Cuboid> getHoles() {
		return cuboids;
	}
	
	/**
	 * Returns a vector representing the normalized normal of the underlying plane.
	 */
	public MathVector getNormal() {
		return plane.getNormal();
	}
	
	/**
	 * Returns the underlying plane of this obstacle. Note that you can access the normal and plane distance directly
	 * using the corresponding methods from SimpleObstacle.
	 */
	public MathPlane3D getPlane() {
		return plane;
	}
}
